/*
 * Ant Colony Optimization
 * Alex Oliveira
 * Heitor Luiz
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "comum.h"
#include "simulacao.h"
#include "vrml.h"

static int qtdformigas = 0;


/*
 * Inicializa as variáveis para uma nova formiga.
 */
Formiga new_formiga() {
	Formiga f;
	f.id = qtdformigas++;
	f.x = terreno.formigueiro.x;
	f.y = terreno.formigueiro.y;
	f.frente = rand() % 8;
	f.encontrou = false;
	f.voltando = false;
	memset(f.caminho, 0, MAX_CAMINHO * sizeof(Ponto));
	f.caminho_length = 0;
	f.vrml = init_formiga(f.id);
	return f;
}


/*
 * Reseta as variáveis de uma formiga.
 */
void del_formiga(Formiga *formiga) {
	formiga->caminho_length = 0;
	formiga->encontrou = false;
	formiga->voltando = false;
	end_formiga(formiga);
}


/*
 * Faz a formiga que está na posição (x,y) soltar ferormônio.
 */
void solta_ferormonio(Formiga *f) {
	int x = f->x;
	int y = f->y;

	// Coloca ferormônio na célula em que a formiga está
	terreno.ferormonio[x][y] += qtdferormonio;

	// Coloca uma quantidade um pouco menor nas células vizinhas
	terreno.ferormonio[x - 1][y - 1]
		= terreno.ferormonio[x - 1][y]
		= terreno.ferormonio[x - 1][y + 1]
		= terreno.ferormonio[x][y - 1]
		= terreno.ferormonio[x][y + 1]
		= terreno.ferormonio[x + 1][y - 1]
		= terreno.ferormonio[x + 1][y]
		= terreno.ferormonio[x + 1][y + 1]
			+= qtdferormonio * .66;

	// Coloca uma quantidade menor ainda nas células a 2 passos de distância
	terreno.ferormonio[x - 2][y - 2]
		= terreno.ferormonio[x - 2][y - 1]
		= terreno.ferormonio[x - 2][y]
		= terreno.ferormonio[x - 2][y + 1]
		= terreno.ferormonio[x - 2][y + 2]
		= terreno.ferormonio[x][y - 2]
		= terreno.ferormonio[x][y + 2]
		= terreno.ferormonio[x + 2][y - 2]
		= terreno.ferormonio[x + 2][y - 1]
		= terreno.ferormonio[x + 2][y]
		= terreno.ferormonio[x + 2][y + 1]
		= terreno.ferormonio[x + 2][y + 2]
			+= qtdferormonio * .33;

	// Garante que a quantidade em nenhuma célula ultrapassou o máximo
	for (int i = -2; i <= 2; i++) {
		for (int j = -2; j <= 2; j++) {
			if (terreno.ferormonio[i][j] > MAX_FERORMONIO) {
				terreno.ferormonio[i][j] = MAX_FERORMONIO;
			}
		}
	}
}


/*
 * Faz a formiga dar o próximo passo, baseada nos custos, na quantidade de
 * ferormônio e no fator aleatório para saber em qual direção será dado o passo.
 */
void prox_passo(Formiga *formiga) {
	int x = formiga->x, y = formiga->y;

	/* Se bateu na parede, volta. */
	if (x == 0 || x == 100 || y == 0 || y
			== 100) {
		formiga->voltando = true;
	}

	/* Se chegar no formigueiro, vai de novo. */
	else if ( ( x == 50 && y == 50 ) || formiga->caminho_length == 0) {
		formiga->voltando = false;
		formiga->encontrou = false;
		formiga->caminho_length = 0;
		formiga->frente = rand() % 8;
		if (rand() % 2 == 0) {	// 50% de chance de já sair do formigueiro na direção certa
			int maior = terreno.ferormonio[49][49];
			formiga->frente = SO;
			if (terreno.ferormonio[49][50] > maior) {
				maior = terreno.ferormonio[49][50];
				formiga->frente = O;
			}
			if (terreno.ferormonio[49][51] > maior) {
				maior = terreno.ferormonio[49][51];
				formiga->frente = NO;
			}
			if (terreno.ferormonio[50][49] > maior) {
				maior = terreno.ferormonio[50][49];
				formiga->frente = S;
			}
			if (terreno.ferormonio[50][51] > maior) {
				maior = terreno.ferormonio[50][51];
				formiga->frente = N;
			}
			if (terreno.ferormonio[51][49] > maior) {
				maior = terreno.ferormonio[50][49];
				formiga->frente = SE;
			}
			if (terreno.ferormonio[51][50] > maior) {
				maior = terreno.ferormonio[51][50];
				formiga->frente = L;
			}
			if (terreno.ferormonio[51][51] > maior) {
				maior = terreno.ferormonio[51][51];
				formiga->frente = NE;
			}
		}
	}

	/* Se achar comida, volta soltando ferormônio. */
	else if (terreno.comida[x][y]) {
		formiga->voltando = true;
		formiga->encontrou = true;

	}

	/* Quando a formiga estiver voltando. */
	if (formiga->voltando) {
		if (formiga->encontrou) {
			solta_ferormonio(formiga);
		}
		formiga->caminho_length--;
		formiga->x = formiga->caminho[formiga->caminho_length].x;
		formiga->y = formiga->caminho[formiga->caminho_length].y;
	}

	/* Quando a formiga estiver indo. */
	else {

		// Descobre quem são as células da esquerda, direita e frente
		Ponto esq, frente, dir;
		switch (formiga->frente) {
		case N:
			esq.x = x - 1;
			frente.x = x;
			dir.x = x + 1;
			esq.y = dir.y = frente.y = y + 1;
			break;
		case NE:
			esq.x = x;
			frente.x = dir.x = x + 1;
			esq.y = frente.y = y + 1;
			dir.y = y;
			break;
		case L:
			esq.x = frente.x = dir.x = x + 1;
			esq.y = y + 1;
			frente.y = y;
			dir.y = y - 1;
			break;
		case SE:
			esq.x = frente.x = x + 1;
			dir.x = x;
			esq.y = y;
			frente.y = dir.y = y - 1;
			break;
		case S:
			esq.x = x + 1;
			frente.x = x;
			dir.x = x - 1;
			esq.y = dir.y = frente.y = y - 1;
			break;
		case SO:
			esq.x = x;
			frente.x = dir.x = x - 1;
			esq.y = frente.y = y - 1;
			dir.y = y;
			break;
		case O:
			esq.x = frente.x = dir.x = x - 1;
			esq.y = y - 1;
			frente.y = y;
			dir.y = y + 1;
			break;
		case NO:
			esq.x = frente.x = x - 1;
			dir.x = x;
			esq.y = y;
			frente.y = dir.y = y + 1;
			break;
		default:
			fputs("DIREÇÃO DESCONHECIDA!", stderr);
			exit(1);
		}

		// Calcula pra onde ir
		float custo_esq = terreno.custo[esq.x][esq.y];
		float custo_frente = terreno.custo[frente.x][frente.y];
		float custo_dir = terreno.custo[dir.x][dir.y];
		if (custo_esq > 50 && custo_frente > 50 && custo_dir > 50) {
			formiga->voltando = true;
		}
		int prob_esq = ( (terreno.ferormonio[esq.x][esq.y] + 1.0) / (custo_esq + 1.0) ) * 1000;
		int prob_frente = ( (terreno.ferormonio[frente.x][frente.y] + 1.0) / (custo_frente + 1.0) ) * 1000;
		int prob_dir = ( (terreno.ferormonio[dir.x][dir.y] + 1.0) / (custo_dir + 1.0) ) * 1000;
		int aleatorio = rand() % (prob_esq + prob_frente + prob_dir);
		if (aleatorio < prob_esq) {
			formiga->x = esq.x;
			formiga->y = esq.y;
			formiga->caminho[formiga->caminho_length++] = esq;
		} else if (aleatorio < (prob_esq + prob_dir)) {
			formiga->x = dir.x;
			formiga->y = dir.y;
			formiga->caminho[formiga->caminho_length++] = dir;
		} else {
			formiga->x = frente.x;
			formiga->y = frente.y;
			formiga->caminho[formiga->caminho_length++] = frente;
		}

		// Decide pra onde olhar
		Direcao olhar = (rand() % 3) + formiga->frente - 1;
		if (olhar < 0) olhar = 7;
		else if (olhar > 7) olhar = 0;
		formiga->frente = olhar;
	}

	grava_passo(formiga);
}


/*
 * Percorre todo o terreno, evaporando o ferormônio.
 */
void evapora() {
	for (int i = 0; i < TAM_LADO; i++) {
		for (int j = 0; j < TAM_LADO; j++) {
			terreno.ferormonio[i][j] -= evaporacao;
			if (terreno.ferormonio[i][j] < 0) {
				terreno.ferormonio[i][j] = 0;
			}
		}
	}
}
